package com.cw.lang.common.framework.feign;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

import org.apache.commons.lang3.ArrayUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;

import com.cw.lang.common.framework.okhttp.LocalInterceptor;
import com.cw.lang.common.framework.utils.RpcAppParamUtil;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.Buffer;
import okio.BufferedSink;
import okio.ByteString;
import okio.Source;
import okio.Timeout;

/**
 * 针对于feign层做日志打印
 *
 * @author chenw
 * @version V2.1
 * @since 2.1.0 2019-03-16 17:47
 */
@Slf4j
@Getter
@Setter
public class RpcLoggingFeignInterceptor implements LocalInterceptor {

    private static final Integer ORDERED = Ordered.HIGHEST_PRECEDENCE + 1000;

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();

        RequestBody requestBody = request.body();
        // 如果requestBody为空，则直接返回
        if (requestBody == null) {
            return chain.proceed(request);
        }
        // 请求路径
        String path = request.url()
            .uri()
            .getPath();

        String requestContent = getRequestContent(requestBody);
        logForRequest(path, requestContent);

        Request newRequest = buildRequest(request, requestBody, requestContent);

        Response response = chain.proceed(newRequest);
        ResponseBody body = response.body();
        // 如果responseBody为空，则直接返回
        if (body == null) {
            return response;
        }
        byte[] bytes = body.bytes();

        logForResponse(path, bytes);

        return buildResponse(response, body, bytes);

    }

    /**
     * 构造新的响应
     *
     * @param response 原响应
     * @param body ResponseBody
     * @param bytes 原响应报文
     * @return 新响应
     */
    private Response buildResponse(Response response, ResponseBody body, byte[] bytes) {
        return response.newBuilder()
            .code(HttpStatus.OK.value())
            .body(ResponseBody.create(body.contentType(), bytes))
            .build();
    }

    /**
     * 出参日志打印
     *
     * @param path 请求url
     * @param bytes 响应报文
     */
    private void logForResponse(String path, byte[] bytes) {
        if (ArrayUtils.isEmpty(bytes)) {
            log.info("请求[{}]的出参为空", path);
        } else {
            log.info("请求[{}]的出参为:[{}]", path, new String(bytes, StandardCharsets.UTF_8));
        }
    }

    /**
     * 针对入参打印日志
     *
     * @param path 请求url
     * @param requestContent 请求参数
     */
    private void logForRequest(String path, String requestContent) {
        if (RpcAppParamUtil.getRpcBaseRequest() != null) {
            log.info("公共参数：{}", RpcAppParamUtil.getRpcBaseRequest());
        }
        if (RpcAppParamUtil.getRpcBaseUserRequest() != null) {
            log.info("用户请求参数：{}", RpcAppParamUtil.getRpcBaseUserRequest());
        }
        log.info("请求[{}]的业务参数入参为:[{}]", path, requestContent);
    }

    /**
     * 构造新的请求
     *
     * @param request 原请求
     * @param requestBody requestBody
     * @param requestContent 原请求的content
     * @return 新request
     */
    private Request buildRequest(Request request, RequestBody requestBody, String requestContent) {
        return new Request.Builder().url(request.url())
            .method(request.method(), RequestBody.create(requestBody.contentType(), requestContent))
            .headers(request.headers())
            .tag(request.tag())
            .build();
    }

    /**
     * 获取请求中的content
     *
     * @param requestBody requestBody
     * @return content
     * @throws IOException 异常
     */
    private String getRequestContent(RequestBody requestBody) throws IOException {
        CopyBufferedSink bufferedRequestBody = new CopyBufferedSink();
        requestBody.writeTo(bufferedRequestBody);
        return bufferedRequestBody.contentAsUtf8();
    }

    @Override
    public int getOrder() {
        return ORDERED;
    }

    /**
     * 该Interceptor使用，仅使用{@link CopyBufferedSink#write(byte[], int, int)}方法，其他方法都是空实现
     */
    private static class CopyBufferedSink implements BufferedSink {

        /** 请求body参数 */
        private byte[] content;

        /**
         * 返回请求body参数，以UTF-8转为String类型
         *
         * @return 请求body参数，以UTF-8转为String类型
         */
        String contentAsUtf8() {
            return new String(content, StandardCharsets.UTF_8);
        }

        @Override
        public Buffer buffer() {
            return null;
        }

        @Override
        public BufferedSink write(ByteString byteString) {
            return null;
        }

        @Override
        public BufferedSink write(byte[] source) {
            return null;
        }

        @Override
        public BufferedSink write(byte[] source, int offset, int byteCount) {
            content = source;
            return this;
        }

        @Override
        public long writeAll(Source source) {
            return 0;
        }

        @Override
        public BufferedSink write(Source source, long byteCount) {
            return null;
        }

        @Override
        public BufferedSink writeUtf8(String string) {
            return null;
        }

        @Override
        public BufferedSink writeUtf8(String string, int beginIndex, int endIndex) {
            return null;
        }

        @Override
        public BufferedSink writeUtf8CodePoint(int codePoint) {
            return null;
        }

        @Override
        public BufferedSink writeString(String string, Charset charset) {
            return null;
        }

        @Override
        public BufferedSink writeString(String string, int beginIndex, int endIndex, Charset charset) {
            return null;
        }

        @Override
        public BufferedSink writeByte(int b) {
            return null;
        }

        @Override
        public BufferedSink writeShort(int s) {
            return null;
        }

        @Override
        public BufferedSink writeShortLe(int s) {
            return null;
        }

        @Override
        public BufferedSink writeInt(int i) {
            return null;
        }

        @Override
        public BufferedSink writeIntLe(int i) {
            return null;
        }

        @Override
        public BufferedSink writeLong(long v) {
            return null;
        }

        @Override
        public BufferedSink writeLongLe(long v) {
            return null;
        }

        @Override
        public BufferedSink writeDecimalLong(long v) {
            return null;
        }

        @Override
        public BufferedSink writeHexadecimalUnsignedLong(long v) {
            return null;
        }

        @Override
        public void write(Buffer source, long byteCount) {

        }

        @Override
        public void flush() {

        }

        @Override
        public Timeout timeout() {
            return null;
        }

        @Override
        public boolean isOpen() {
            return false;
        }

        @Override
        public void close() {

        }

        @Override
        public BufferedSink emit() {
            return null;
        }

        @Override
        public BufferedSink emitCompleteSegments() {
            return null;
        }

        @Override
        public OutputStream outputStream() {
            return null;
        }

        @Override
        public int write(ByteBuffer src) {
            return 0;
        }

        @Override
        public Buffer getBuffer() {
            return null;
        }

        @Override
        public BufferedSink write(@NotNull ByteString byteString, int i, int i1) {
            return null;
        }
    }
}
